#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
EasyFTP - 服务器端实现
v1.0版本
"""

import os
import json
import logging
import argparse
import random
import string
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer


class FtpServer:
    """FTP服务器类，负责启动和管理FTP服务器"""

    def __init__(self, config_file='../config/config.json'):
        """初始化FTP服务器配置"""
        # 加载配置文件
        self.config = self._load_config(config_file)
        # 保存配置文件路径，用于后续更新
        self.config_file = config_file
        # 设置日志
        self._setup_logging()
        # 服务器实例
        self.server = None
        # 设备验证码
        self.device_code = None
        # 设备ID
        self.device_id = self.config['server'].get('device_id', 'default_device')

    def _load_config(self, config_file):
        """加载配置文件"""
        try:
            with open(config_file, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            print(f"加载配置文件失败: {e}")
            # 返回默认配置
            return {
                "server": {
                    "host": "0.0.0.0",
                    "port": 2121,
                    "username": "user",
                    "password": "password",
                    "shared_dir": "./shared",
                    "passive_mode": True,
                    "passive_ports": {"start": 60000, "end": 65535}
                }
            }

    def _setup_logging(self):
        """设置日志配置，确保终端显示客户端连接和命令信息"""
        # 从配置中获取日志级别，默认为INFO
        log_level = getattr(logging, self.config.get("logging", {}).get("level", "INFO").upper(), logging.INFO)
        # 从配置中获取日志文件路径
        log_file = self.config.get("logging", {}).get("file", "ftp_transfer.log")
        
        # 配置日志格式，包含时间、来源、级别和消息
        log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        
        # 获取根日志器
        root_logger = logging.getLogger()
        # 清除已有的处理器，避免重复输出
        root_logger.handlers = []
        
        # 设置日志级别
        root_logger.setLevel(log_level)
        
        # 创建文件处理器（写入日志文件）
        file_handler = logging.FileHandler(log_file)
        file_handler.setFormatter(logging.Formatter(log_format))
        root_logger.addHandler(file_handler)
        
        # 创建控制台处理器（输出到终端）
        console_handler = logging.StreamHandler()
        console_handler.setFormatter(logging.Formatter(log_format))
        root_logger.addHandler(console_handler)
        
        # 创建服务器专用日志器
        self.logger = logging.getLogger("FTPServer")

    def _generate_device_code(self):
        """生成8位随机设备验证码，包含大小写字母、数字和符号"""
        characters = string.ascii_letters + string.digits + "!@#$%^&*()"
        return ''.join(random.choice(characters) for _ in range(8))
        
    def _update_config(self):
        """更新配置文件，保存设备ID和设备验证码"""
        try:
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(self.config, f, indent=2, ensure_ascii=False)
            self.logger.info(f"配置文件已更新: {self.config_file}")
        except Exception as e:
            self.logger.error(f"更新配置文件失败: {e}")

    def start(self):
        """启动FTP服务器"""
        try:
            # 获取服务器配置
            server_config = self.config["server"]
            
            # 如果用户通过命令行指定了设备ID，则使用该ID
            if hasattr(self, 'cli_device_id') and self.cli_device_id:
                self.device_id = self.cli_device_id
                self.config['server']['device_id'] = self.device_id
                self.logger.info(f"使用命令行指定的设备ID: {self.device_id}")
            
            # 生成新的设备验证码
            self.device_code = self._generate_device_code()
            self.config['server']['device_code'] = self.device_code
            
            # 更新配置文件
            self._update_config()
            
            # 打印设备ID和验证码到终端，并使用日志记录
            print("=" * 60)
            print(f"  设备ID: {self.device_id}")
            print(f"  设备验证码: {self.device_code}")
            print("=" * 60)
            print("请将上述信息提供给客户端用户进行连接验证")
            # 使用logger.info记录设备ID和验证码，确保能被主进程捕获
            self.logger.info(f"设备ID: {self.device_id}")
            self.logger.info(f"设备验证码: {self.device_code}")
            
            # 创建用户授权器 - 使用设备ID和验证码作为用户名和密码
            authorizer = DummyAuthorizer()
            
            shared_dir = server_config["shared_dir"]
            
            # 确保共享目录存在
            if not os.path.exists(shared_dir):
                os.makedirs(shared_dir)
                self.logger.info(f"创建共享目录: {shared_dir}")
            
            # 为设备ID添加权限 "elradfmwMT"：
            # e = 更改目录, l = 列出文件, r = 读取文件, a = 追加数据
            # d = 删除文件, f = 重命名文件, m = 创建目录, w = 写入文件
            # M = 修改文件模式, T = 修改文件时间戳
            authorizer.add_user(self.device_id, self.device_code, shared_dir, perm="elradfmwMT")
            
            # 创建处理器
            class CustomFTPHandler(FTPHandler):
                """自定义FTP处理器，用于记录客户端操作日志，包括IP、设备ID和命令信息"""
                # 定义日志器
                logger = logging.getLogger("FTPServer")
                
                def on_connect(self):
                    """客户端连接时记录日志"""
                    self.logger.info(f"客户端连接: IP={self.remote_ip}, 端口={self.remote_port}")
                
                def on_disconnect(self):
                    """客户端断开连接时记录日志"""
                    # 如果用户已登录，记录设备ID信息
                    device_id = getattr(self, 'device_id', '未登录')
                    self.logger.info(f"客户端断开连接: IP={self.remote_ip}, 设备ID={device_id}")
                
                def on_login(self, device_id):
                    """客户端登录成功时记录日志（不显示验证码）"""
                    self.logger.info(f"客户端登录成功: IP={self.remote_ip}, 设备ID={device_id}")
                    # 保存设备ID以供后续使用
                    self.device_id = device_id
                    
                def on_login_failed(self, device_id, password):
                    """客户端登录失败时记录日志"""
                    self.logger.warning(f"客户端登录失败: IP={self.remote_ip}, 设备ID={device_id}")
                
                def pre_process_command(self, line, cmd, arg):
                    """处理客户端命令前记录日志"""
                    # 获取设备ID（如果已登录）
                    device_id = getattr(self, 'device_id', '未登录')
                    self.logger.info(f"客户端命令: IP={self.remote_ip}, 设备ID={device_id}, 命令={cmd}, 参数={arg}")
                    return super().pre_process_command(line, cmd, arg)
                
                def ftp_LIST(self, path):
                    """记录LIST命令"""
                    device_id = getattr(self, 'device_id', '未登录')
                    self.logger.info(f"客户端操作: IP={self.remote_ip}, 设备ID={device_id}, 列出目录={path or '.'}")
                    super().ftp_LIST(path)
                
                def ftp_STOR(self, file):
                    """记录上传文件命令"""
                    device_id = getattr(self, 'device_id', '未登录')
                    self.logger.info(f"客户端操作: IP={self.remote_ip}, 设备ID={device_id}, 上传文件={file}")
                    super().ftp_STOR(file)
                
                def ftp_RETR(self, file):
                    """记录下载文件命令"""
                    device_id = getattr(self, 'device_id', '未登录')
                    self.logger.info(f"客户端操作: IP={self.remote_ip}, 设备ID={device_id}, 下载文件={file}")
                    super().ftp_RETR(file)
                
                def ftp_MKD(self, path):
                    """记录创建目录命令"""
                    device_id = getattr(self, 'device_id', '未登录')
                    self.logger.info(f"客户端操作: IP={self.remote_ip}, 设备ID={device_id}, 创建目录={path}")
                    super().ftp_MKD(path)
                
                def ftp_RMD(self, path):
                    """记录删除目录命令"""
                    device_id = getattr(self, 'device_id', '未登录')
                    self.logger.info(f"客户端操作: IP={self.remote_ip}, 设备ID={device_id}, 删除目录={path}")
                    super().ftp_RMD(path)
                
            # 设置处理器
            handler = CustomFTPHandler
            handler.authorizer = authorizer
            
            # 启用被动模式
            if server_config.get("passive_mode", True):
                handler.passive_ports = range(
                    server_config["passive_ports"]["start"],
                    server_config["passive_ports"]["end"] + 1
                )
                self.logger.info(f"启用被动模式，端口范围: {server_config['passive_ports']['start']}-{server_config['passive_ports']['end']}")
            
            # 设置欢迎信息
            handler.banner = "欢迎使用双向FTP文件传输系统！"
            
            # 创建并启动服务器
            address = (server_config["host"], server_config["port"])
            self.server = FTPServer(address, handler)
            
            self.logger.info(f"FTP服务器启动成功，监听地址: {server_config['host']}:{server_config['port']}")
            self.logger.info(f"共享目录: {os.path.abspath(shared_dir)}")
            self.logger.info(f"设备ID: {self.device_id}")
            
            # 开始服务
            self.server.serve_forever()
            
        except Exception as e:
            self.logger.error(f"服务器启动失败: {e}")
            raise
    
    def stop(self):
        """停止FTP服务器"""
        if self.server:
            self.logger.info("正在停止FTP服务器...")
            self.server.close_all()
            self.logger.info("FTP服务器已停止")


def main():
    """主函数"""
    # 解析命令行参数
    parser = argparse.ArgumentParser(description='双向FTP文件传输系统 - 服务器端')
    parser.add_argument('-c', '--config', default='../config/config.json', help='配置文件路径')
    parser.add_argument('-d', '--device-id', help='指定设备ID（可选）')
    args = parser.parse_args()
    
    # 创建并启动服务器
    ftp_server = FtpServer(args.config)
    
    # 如果用户指定了设备ID，则设置到服务器实例
    if args.device_id:
        ftp_server.cli_device_id = args.device_id
    
    try:
        ftp_server.start()
    except KeyboardInterrupt:
        print("\n接收到中断信号，正在停止服务器...")
    finally:
        ftp_server.stop()


if __name__ == '__main__':
    main()